/**
 * @file HistoryData.c
 * @brief 
 * @author xuesong (wenshaoliu@foxmail.com)
 * @version 1.0
 * @date 2024-10-12
 * 
 * @copyright Copyright (c) 2024  by xuesong
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2024-10-12     <td>1.0     <td>xuesong   <td>内容
 * </table>
 */
#include <string.h>
#include "HistoryData.h"
#include "log.h"
//-------------------------------------------------------------------------------------------
#define MI_LOG_INFO(...)    LOG(__VA_ARGS__)
#define MI_LOG_HEXDUMP(...) LOG_DUMP(__VA_ARGS__)
//-------------------------------------------------------------------------------------------
#define U32_MAX     0xFFFFFFFF
#define ONE_HOUR_SECOND 3600
//-------------------------------------------------------------------------------------------
TempHumidityStatistic_t TempHumidityStatistic; // 统计接口
FlashHistory_t FlashHistoryIndex;              // Flash 读写参数
//-------------------------------------------------------------------------------------------
void flash_read_page(unsigned long addr, unsigned long len, unsigned char *buf)
{
    FILE *readFlashFp = fopen("G:/CPP_Code/source/TelinkHistory/Read.bin", "rb");
    if(readFlashFp == NULL)
    {
        LOG("read file err\r\n");
        return;
    }
    fseek(readFlashFp, addr - HISTORY_START_ADDR, SEEK_SET); // 重置文件指针到读取起始位置
    fread(buf, 1, len, readFlashFp);
    fclose(readFlashFp);
}
void flash_write_page(unsigned long addr, unsigned long len, unsigned char *buf)
{
    FILE *WriteFlashFp = fopen("G:/CPP_Code/source/TelinkHistory/Read.bin", "rb+");
    if (WriteFlashFp == NULL)
    {
        LOG("write open file err\r\n");
        return;
    }
    fseek(WriteFlashFp, addr - HISTORY_START_ADDR, SEEK_SET); // 重置文件指针到读取起始位置
    fwrite(buf, 1, len, WriteFlashFp);
    fclose(WriteFlashFp);
}

void flash_erase_sector(unsigned long addr)
{
    MI_LOG_INFO("flash erase sector =%x\r\n", addr);
    if ((addr % FLASH_SECTOR_SIZE) != 0)
    {
        MI_LOG_INFO("flash erase sector addr err =%x\r\n", addr);
        return;
    }
    uint16_t addrOffset = 0;
    uint8_t dataBuf[256] = {0xFF};
    memset(dataBuf, 0xFF, 256);
    for (; addrOffset < FLASH_SECTOR_SIZE;addrOffset+=256)
    {
        flash_write_page(addr + addrOffset, 256, dataBuf);
    }
}
//-------------------------------------------------------------------------------------------
void static CheckFreeOneHistorySector(void)
{
#if (HISTORY_MAX_SECTOR > 6)
    if (FlashHistoryIndex.EmptySectorCnt < (HISTORY_MAX_SECTOR - 6))
#else
    if (FlashHistoryIndex.EmptySectorCnt < 1)
#endif
    {
        FlashHistoryIndex.EmptySectorCnt++;
        flash_erase_sector(FlashHistoryIndex.StartAddr);
        FlashHistoryIndex.StartAddr += FLASH_SECTOR_SIZE;
        if (FlashHistoryIndex.StartAddr >= HISTORY_END_ADDR)
        {
            FlashHistoryIndex.StartAddr = HISTORY_START_ADDR;
        }
    }
    // MI_LOG_INFO("history startAddr=0x%X,currentAddr=0x%X,emptySector=%d\r\n", FlashHistoryIndex.StartAddr, FlashHistoryIndex.CurrentAddr, FlashHistoryIndex.EmptySectorCnt);
    printf("history startAddr=0x%X,currentAddr=0x%X,emptySector=%d\r\n", FlashHistoryIndex.StartAddr - HISTORY_START_ADDR, FlashHistoryIndex.CurrentAddr - HISTORY_START_ADDR, FlashHistoryIndex.EmptySectorCnt);
}

int WritRecordData2Flash(TempHumidityHistory_t historyData)
{
    flash_write_page(FlashHistoryIndex.CurrentAddr, SINGLE_HISTORY_DATA_LEN, &historyData);
    FlashHistoryIndex.CurrentAddr += SINGLE_HISTORY_DATA_LEN;
    if((FlashHistoryIndex.CurrentAddr % FLASH_SECTOR_SIZE) == 0)
    {
        FlashHistoryIndex.EmptySectorCnt--;
        if(FlashHistoryIndex.CurrentAddr >= HISTORY_END_ADDR)
            FlashHistoryIndex.CurrentAddr = HISTORY_START_ADDR;
        if (FlashHistoryIndex.EmptySectorCnt < 0){
            CheckFreeOneHistorySector();                //蓝牙连接状态也必须处理否则存储会溢出
        }
    }
    CheckFreeOneHistorySector();                        //值得处理，如果蓝牙连接状态则暂时不处理
}

void InitFlashHistoryIndex(void)
{
    TempHumidityHistory_t HistoryDataMin;
    FlashHistory_t tempFlashHistoryIndex;
    uint32_t minHistoryUtc = U32_MAX;
    uint32_t maxHistoryUtc = 0;
    tempFlashHistoryIndex.StartAddr = HISTORY_START_ADDR;
    tempFlashHistoryIndex.CurrentAddr = HISTORY_START_ADDR;
    tempFlashHistoryIndex.EmptySectorCnt = HISTORY_MAX_SECTOR;

    FlashHistoryIndex.StartAddr = tempFlashHistoryIndex.StartAddr;
    for (uint8_t index = 0; index < HISTORY_MAX_SECTOR; index++) // find start addr and current sector addr
    {
        flash_read_page(tempFlashHistoryIndex.StartAddr, SINGLE_HISTORY_DATA_LEN, &HistoryDataMin);
        MI_LOG_INFO("find Sector %d,Addr=0x%X:", index, tempFlashHistoryIndex.StartAddr);
        MI_LOG_HEXDUMP(&HistoryDataMin, SINGLE_HISTORY_DATA_LEN);
        if (HistoryDataMin.utcTime == U32_MAX)
        {
            tempFlashHistoryIndex.StartAddr += FLASH_SECTOR_SIZE;
            continue;
        }
        tempFlashHistoryIndex.EmptySectorCnt--; // 有一个 Sector 被使用了 4k
        if (minHistoryUtc > HistoryDataMin.utcTime)
        {
            FlashHistoryIndex.StartAddr = tempFlashHistoryIndex.StartAddr; // get history start addr
            minHistoryUtc = HistoryDataMin.utcTime;
        }
        if (maxHistoryUtc < HistoryDataMin.utcTime)
        {
            tempFlashHistoryIndex.CurrentAddr = tempFlashHistoryIndex.StartAddr; // get current sector addr
            maxHistoryUtc = HistoryDataMin.utcTime;
        }
        tempFlashHistoryIndex.StartAddr += FLASH_SECTOR_SIZE;
    }
    FlashHistoryIndex.EmptySectorCnt = tempFlashHistoryIndex.EmptySectorCnt;
    FlashHistoryIndex.CurrentAddr = tempFlashHistoryIndex.CurrentAddr; // 记录当前正在使用Sector起始地址
    maxHistoryUtc = 0;
    minHistoryUtc = U32_MAX;
    int16_t BigIndex = FLASH_SECTOR_SIZE / SINGLE_HISTORY_DATA_LEN;
    int16_t litterIndex = 0;
    bool CurrentFlashExit = false;
    uint16_t currentIndex;
    BigIndex -= 1;
    while (litterIndex <= BigIndex)
    {
        currentIndex = (BigIndex + litterIndex) >> 1;
        tempFlashHistoryIndex.CurrentAddr = FlashHistoryIndex.CurrentAddr+(currentIndex * SINGLE_HISTORY_DATA_LEN);
        flash_read_page(tempFlashHistoryIndex.CurrentAddr, SINGLE_HISTORY_DATA_LEN, &HistoryDataMin);
        MI_LOG_INFO("find addr 0x%X:", tempFlashHistoryIndex.CurrentAddr);
        MI_LOG_HEXDUMP(&HistoryDataMin, SINGLE_HISTORY_DATA_LEN);
        if (HistoryDataMin.utcTime == U32_MAX)
        {
            CurrentFlashExit = false;
            BigIndex = currentIndex - 1;
        }
        else
        {
            CurrentFlashExit = true;
            litterIndex = currentIndex + 1;
        }
    }

    if (CurrentFlashExit) // 当前点有数据
    {
        tempFlashHistoryIndex.CurrentAddr += SINGLE_HISTORY_DATA_LEN;
    }
    if ((tempFlashHistoryIndex.CurrentAddr - FlashHistoryIndex.CurrentAddr) >= FLASH_SECTOR_SIZE)
    {
        if (tempFlashHistoryIndex.CurrentAddr >= HISTORY_END_ADDR)
        {
            tempFlashHistoryIndex.CurrentAddr = HISTORY_START_ADDR;
        }
    }
    FlashHistoryIndex.CurrentAddr = tempFlashHistoryIndex.CurrentAddr;
    CheckFreeOneHistorySector();
}
/**
 * @brief 
 * @param  FlashIndex       查找数据历史记录结构体
 * @param  startTime        查找历史记录开始时间s
 * @return int8_t           1：有符合历史数据，0：无符合要求历史数据
 */
int8_t FindHistoryStartIndex(FlashHistory_t *FlashIndex,time_t startTime)
{
    FlashHistory_t tempHistory;
    TempHumidityHistory_t historyData;
    uint32_t startAddr = 0;
    bool StartAddrRecord = false;
    memcpy(&tempHistory, FlashIndex, sizeof(FlashHistory_t));
    for(uint8_t index = 0; index < HISTORY_MAX_SECTOR; index++)
    {
        flash_read_page(tempHistory.StartAddr, SINGLE_HISTORY_DATA_LEN, &historyData);
        if(historyData.utcTime != U32_MAX)
        {
            if(historyData.utcTime <= startTime) //查找下一个 sector
            {
                if(tempHistory.StartAddr > startAddr)
                    startAddr = tempHistory.StartAddr;
                else
                    break;
                tempHistory.StartAddr += FLASH_SECTOR_SIZE;
                if(tempHistory.StartAddr >= HISTORY_END_ADDR)
                    tempHistory.StartAddr = HISTORY_START_ADDR;
            }else{
                if(startAddr == 0)
                    startAddr = tempHistory.StartAddr;
                break;                          //起始不在当前 sector
            }
        }else{
            break;
        }
    }
    if(!startAddr)
        return false;   //没有合适历史数据

    int16_t BigIndex = FLASH_SECTOR_SIZE / SINGLE_HISTORY_DATA_LEN;
    int16_t litterIndex = 0;
    uint16_t currentIndex;
    tempHistory.StartAddr = startAddr;
    BigIndex -= 1;
    while (litterIndex <= BigIndex)
    {
        currentIndex = (BigIndex + litterIndex) >> 1;
        startAddr = tempHistory.StartAddr + (currentIndex * SINGLE_HISTORY_DATA_LEN);
        flash_read_page(startAddr, SINGLE_HISTORY_DATA_LEN, &historyData);
        MI_LOG_INFO("find addr 0x%X:", startAddr);
        MI_LOG_HEXDUMP(&historyData, SINGLE_HISTORY_DATA_LEN);
        if (historyData.utcTime > startTime)
        {
            BigIndex = currentIndex - 1;
        }
        else
        {
            litterIndex = currentIndex + 1;
            if(historyData.utcTime == startTime)
                break;                          //找到当前值
        }
    }
    FlashIndex->StartAddr = startAddr;
    return true;
}

#include <time.h>
#include <unistd.h>
// #define UTC_OFFSET_BEIJING        (8*3600)
#define UTC_OFFSET_BEIJING        (0)
void TestHistory(void)
{
    bool needBreak = true;
    FlashHistory_t tempHistory;
    time_t nowTime = 0;
    time_t TestTime = 0;
    struct tm *pNowDate;
    struct tm testStartDate ={
        .tm_year =(2024-1900),
        .tm_mon = 3,
        .tm_mday = 28,
        .tm_hour = 0,
        .tm_min = 0,
        .tm_sec = 0,
    };
    TempHumidityHistory_t historyData;
    tempHistory.StartAddr = HISTORY_START_ADDR;
    while(1)
    {
        if(needBreak || tempHistory.StartAddr >= HISTORY_END_ADDR)
            break;
        flash_erase_sector(tempHistory.StartAddr);
        tempHistory.StartAddr += FLASH_SECTOR_SIZE;
    }

    TestTime = mktime(&testStartDate);
    time(&nowTime);
    pNowDate = localtime(&nowTime);
    InitFlashHistoryIndex();
    memcpy(&tempHistory, &FlashHistoryIndex, sizeof(FlashHistoryIndex));
    tempHistory.CurrentAddr -= SINGLE_HISTORY_DATA_LEN;
    if(tempHistory.CurrentAddr < HISTORY_START_ADDR)
    {
        if(tempHistory.EmptySectorCnt == HISTORY_MAX_SECTOR)
            tempHistory.CurrentAddr = HISTORY_START_ADDR;
        else
            tempHistory.CurrentAddr = HISTORY_END_ADDR - SINGLE_HISTORY_DATA_LEN;
    }
    flash_read_page(tempHistory.CurrentAddr, SINGLE_HISTORY_DATA_LEN, &historyData);
    if(historyData.utcTime == U32_MAX)
    {
        nowTime = TestTime;
        historyData.Temperature = 1;
        historyData.Humidity = 1;
        printf("last data utc err,start as:0x%X\r\n",nowTime);
    }else{
        historyData.utcTime += UTC_OFFSET_BEIJING;
        if(historyData.utcTime > nowTime)
        {
            nowTime = historyData.utcTime + 3600;
        }
    }
    nowTime /= 3600;    //正小时记录
    nowTime *= 3600;

    needBreak = true;
    while(1)                                        //Test Write history data
    {
        if(needBreak)
            break;
        pNowDate = localtime(&nowTime);
        historyData.utcTime = nowTime;
        historyData.Temperature += 1;
        if(historyData.Temperature >500)
            historyData.Temperature = 1;
        historyData.Humidity += 1;
        if(historyData.Humidity > 100)
            historyData.Humidity = 1;
        WritRecordData2Flash(historyData);
        usleep(100); // 延时x us
        nowTime+=3600;
    }
    needBreak = false;
    memcpy(&tempHistory, &FlashHistoryIndex, sizeof(FlashHistoryIndex));
    tempHistory.CurrentAddr = FlashHistoryIndex.StartAddr;

    FILE *logText = fopen("G:/CPP_Code/source/TelinkHistory/ReadData.txt", "w");
    if(logText)
    {
        fprintf(logText, "FlashAddr\tValue\t\t\t\t\t\tDate\t\t\t\t\ttemperature/℃\t\tHumidity/%%\n");
    }else{
        printf("logTxt Err\r\n");
    }
    while (1)
    {
        if (needBreak)
            break;
        flash_read_page(tempHistory.CurrentAddr, SINGLE_HISTORY_DATA_LEN, &historyData);
        if (historyData.utcTime == U32_MAX)
            break;
        nowTime = historyData.utcTime + UTC_OFFSET_BEIJING;
        pNowDate = localtime(&nowTime);
        if(logText)
        {
            fprintf(logText, "0x%04x\t\t",tempHistory.CurrentAddr - HISTORY_START_ADDR);
            uint8_t *pData = (uint8_t *)&historyData;
            for (uint8_t i = 0; i < sizeof(historyData);i++)
            {
                fprintf(logText, "%02X ", *pData++);
            }
            fprintf(logText, "\t");
            fprintf(logText, "%04d/%02d/%02d %02d:%02d:%02d\t\t%0.2f\t\t\t\t%d%%\n",pNowDate->tm_year + 1900, pNowDate->tm_mon + 1, pNowDate->tm_mday, pNowDate->tm_hour, pNowDate->tm_min, pNowDate->tm_sec,
                    historyData.Temperature / 10.0, historyData.Humidity);
        }
        else{
            printf("flash addr=0x%X,history Date:%d/%d/%d %d:%d:%d,temperature=%0.2f℃,humidity=%d%%\r\n", tempHistory.CurrentAddr-HISTORY_START_ADDR,\
            pNowDate->tm_year+1900, pNowDate->tm_mon+1, pNowDate->tm_mday, pNowDate->tm_hour, pNowDate->tm_min,pNowDate->tm_sec,\
            historyData.Temperature/10.0,historyData.Humidity);
        }
        tempHistory.CurrentAddr += SINGLE_HISTORY_DATA_LEN;
        if (tempHistory.CurrentAddr >= HISTORY_END_ADDR)
            tempHistory.CurrentAddr = HISTORY_START_ADDR;
    }
    if(logText)
    {
        fclose(logText);
    }

    struct tm InputDate;
    while(1)
    {
        printf("输入查询历史记录起始时间,格式如下 YYYY/MM/DD HH:MM:SS:");
        scanf("%d/%d/%d %d:%d:%d", &InputDate.tm_year, &InputDate.tm_mon, &InputDate.tm_mday, &InputDate.tm_hour, &InputDate.tm_min, &InputDate.tm_sec);
        memcpy(&tempHistory, &FlashHistoryIndex, sizeof(FlashHistoryIndex));
        InputDate.tm_year -= 1900;
        InputDate.tm_mon -= 1;
        nowTime = mktime(&InputDate);
        nowTime -= UTC_OFFSET_BEIJING;
        printf("find start time =0x%X\r\n", nowTime);
        if(!FindHistoryStartIndex(&tempHistory,nowTime))
            continue;

        needBreak = true;
        while (1)
        {
            if (needBreak)
                break;
            flash_read_page(tempHistory.StartAddr, SINGLE_HISTORY_DATA_LEN, &historyData);
            if (historyData.utcTime == U32_MAX)
                break;
            nowTime = historyData.utcTime + UTC_OFFSET_BEIJING;
            pNowDate = localtime(&nowTime);
            printf("addr=0x%X,history Date:%d/%d/%d %d:%d:%d,temperature=%0.2f℃,humidity=%d%%\r\n", tempHistory.StartAddr-HISTORY_START_ADDR,\
            pNowDate->tm_year+1900, pNowDate->tm_mon+1, pNowDate->tm_mday, pNowDate->tm_hour, pNowDate->tm_min,pNowDate->tm_sec,\
            historyData.Temperature/10.0,historyData.Humidity);

            tempHistory.StartAddr += SINGLE_HISTORY_DATA_LEN;
            if (tempHistory.StartAddr >= HISTORY_END_ADDR)
                tempHistory.StartAddr = HISTORY_START_ADDR;
        }

#define HISTORY_BUFF_SIZE   256
        uint8_t historyDataBuff[HISTORY_BUFF_SIZE];
        uint8_t nextHistory = 0;
        uint32_t historyCnt;
        uint32_t index;
        FILE *logBin = fopen("G:/CPP_Code/source/TelinkHistory/ReadDataHex.bin", "rb+");
        if(!logBin)
        {
            printf("logHexTxt Err\r\n");
        }
        HistorySync_t    HistorySync;
        HistorySync.flashReadAddr = tempHistory.StartAddr;
        while (1)
        {
            if (needBreak)
                break;
           if(HistorySync.flashReadAddr != FlashHistoryIndex.CurrentAddr)
            {
                if(HistorySync.flashReadAddr < FlashHistoryIndex.CurrentAddr)
                {
                    historyCnt = FlashHistoryIndex.CurrentAddr - HistorySync.flashReadAddr;
                    if(historyCnt > HISTORY_BUFF_SIZE)
                    {
                        historyCnt = HISTORY_BUFF_SIZE;
                    }
                    index = 0;
                    flash_read_page(HistorySync.flashReadAddr,historyCnt,&historyDataBuff[index]);
                    index = historyCnt;
                }else if(HistorySync.flashReadAddr > FlashHistoryIndex.CurrentAddr){
                    historyCnt = HISTORY_END_ADDR - HistorySync.flashReadAddr;
                    if(historyCnt > HISTORY_BUFF_SIZE)
                    {
                        historyCnt = HISTORY_BUFF_SIZE;
                    }
                    index = 0;
                    flash_read_page(HistorySync.flashReadAddr,historyCnt,&historyDataBuff[index]);
                    index = historyCnt;
                    if(historyCnt < HISTORY_BUFF_SIZE)
                    {
                        nextHistory = FlashHistoryIndex.CurrentAddr - HISTORY_START_ADDR - historyCnt;
                        if(nextHistory)
                        {
                            if(nextHistory > (HISTORY_BUFF_SIZE - historyCnt))
                                nextHistory = HISTORY_BUFF_SIZE - historyCnt;
                            flash_read_page(HISTORY_START_ADDR,nextHistory,&historyDataBuff[index]);
                            index = index + nextHistory;
                        }
                    }
                }
            }else{
                HistorySync.Flag.Bits.syncHistory = false;
                break;
            }
            if(index)
            {
                if(logBin)
                    fwrite(historyDataBuff, 1, index, logBin);
                HistorySync.flashReadAddr = HistorySync.flashReadAddr + index;
                if(HistorySync.flashReadAddr > HISTORY_END_ADDR)
                {
                    HistorySync.flashReadAddr -= HISTORY_END_ADDR;
                    HistorySync.flashReadAddr += HISTORY_START_ADDR;
                }
            }
        }
        if(logBin)
            fclose(logBin);
    }
}

